package com.ebupt.migu.music.schedule.service.impl;

import cn.hutool.core.io.file.FileReader;
import com.ebupt.migu.common.util.DateUtil;
import com.ebupt.migu.music.commons.sms.Sender;
import com.ebupt.migu.music.commons.utils.SCPAsync;
import com.ebupt.migu.music.schedule.mapper.PTTaskMapper;
import com.ebupt.migu.music.schedule.service.PTTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @description: 白金会员发送短信
 * @author: liuyangyang@ebupt.com
 * @date: 2021/1/18 10:32
 */
@Slf4j
@Service
public class PTTaskServiceImpl implements PTTaskService {

    @Autowired
    private Sender sender;
    @Autowired
    private SCPAsync scpAsync;
    @Autowired
    private PTTaskMapper ptTaskMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${pt_sms_file_path_prefix}")
    private String FILE_PATH_PREFIX;
    @Value("${sms_send_record_path}")
    private String SMS_SEND_RECORD_PATH;

    private static final Pattern pattern = Pattern.compile("(?<=\\{)[^\\}]+");

    /**
     * 定时发送短信
     */
    @Override
    public void send_pt_message() throws Exception {
        if (!hourMinuteBetween()) {
            log.info("当前时间不可进行短信发送，任务结束");
            return;
        }
        // Redis计数用
        String dailyFilesCount = "tac_pt_sms_" + DateUtil.getDateToday();
        Map<String, Object> tacInfo = ptTaskMapper.tac_info();
        if (null == tacInfo || null == tacInfo.get("id")) {
            log.info("当前无待执行白金会员短信下发策略");
            return;
        }
        // 策略ID
        String tacID = String.valueOf(tacInfo.get("id"));
        log.info("执行策略：{}", tacInfo.get("tacName"));

        // 将策略状态修改为进行中，更新执行时间
        ptTaskMapper.tac_info_update(tacID, 5, DateUtil.getDateNow(), "开始执行");

        Map<String, Object> tacDetail = ptTaskMapper.tac_detail(tacID);
        if (null == tacDetail || null == tacDetail.get("id")) {
            log.error("当前策略无策略明细内容：{}", tacID);
            ptTaskMapper.tac_info_update(tacID, 1, null, "tac_pt_sms_detail无关联明细");
            return;
        }
        String templateID = String.valueOf(tacDetail.get("templateId"));
        // 获取短信参数
        String[] params = getParams(String.valueOf(tacDetail.get("smsVar")));
        String path = FILE_PATH_PREFIX + tacDetail.get("fileName");
        log.info("模板ID：{}， 模板参数：【{}】， 文件路径：{}", templateID, Arrays.toString(params), path);
        // redis黑名单
        String redisKey = "";
        if (null != tacDetail.get("redisKey") && !"".equals(String.valueOf(tacDetail.get("redisKey")))) {
            redisKey = String.valueOf(tacDetail.get("redisKey"));
            log.info("策略包含Reids黑名单：{}", redisKey);
        }
        // 千人千面标识
        String sendType = String.valueOf(tacInfo.get("sendType"));
        // 记录文件内容
        String fileName = "a_10008_80029_" + DateUtil.getDateToday() + "_scene_" + tacID + ".dat";
        String recordPath = "/nfs_shared/fine-operation/sms-send-record-label/" + fileName;
        File recordFile = new File(recordPath);
        if (!recordFile.exists()) {
            recordFile.createNewFile();
            log.info("创建文件");
        }
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(recordFile), "utf-8"));
        // 将原文件修改为csv类型，并返回文件名列表
//        String[] files = fileReName(path);
        String[] files = fileList(path);
        if (null == files || files.length == 0) {
            log.error("当前路径下没有可执行文件");
            ptTaskMapper.tac_info_update(tacID, 1, null, "路径下没有可执行文件");
            return;
        }
        for (String file : files) {
            int fileCount = 0;
            if (redisTemplate.hasKey(dailyFilesCount)) {
                fileCount = Integer.valueOf(String.valueOf(redisTemplate.opsForValue().get(dailyFilesCount))).intValue();
                log.info("当前已经执行{}个文件", fileCount);
            }
            // 每日最多执行40个文件，单文件10w人群，总计单日400万人
            if (fileCount >= 40) {
                log.info("今日短信发送量饱和，任务暂停");
                ptTaskMapper.tac_info_update(tacID, 4, null, "短信发送量饱和");
                log.info("上传文件：{}", recordPath);
                scpAsync.scpFile(recordPath);
                log.info("设置统计KEY过期时间");
                setExpireTime(dailyFilesCount, tacID);
                return;
            }
            // 文件计数
            redis_incr(dailyFilesCount);
            log.info("开始执行文件：{}", file);
            String filePath = path + "/" + file;
            // 文件中的用户
            List<String[]> msisdns = getFileData(filePath);
//            List<String[]> msisdns = CSVUtils.csvRead(filePath);
            // 临时人群，每发送一个则在temp中删除当前用户
//            List<String[]> temp = CSVUtils.csvRead(filePath);
            for (String[] msisdn : msisdns) {
                if (msisdn.length == 0 || "".equals(String.valueOf(msisdn[0]))) {
                    continue;
                }
                // 过滤Redis名单
                if (null != redisKey && !"".equals(redisKey) && redisTemplate.opsForSet().isMember(redisKey, String.valueOf(msisdn[0]))) {
                    log.info("用户：{}在Redis黑名单中，不进行下发", msisdn[0]);
                    continue;
                }
                log.info("写入记录：{}", msisdn[0]);
                bufferedWriter.write(String.valueOf(msisdn[0]));
                bufferedWriter.newLine();
                if ("1".equals(sendType)) {
                    params = getParams(msisdn);
                }
                sender.sendSMS(tacID, templateID, msisdn, params,tacInfo);
//                temp.remove(msisdn);
            }
            // 发送完成后删除文件
            deleteFile(filePath);
        }
        if (null != bufferedWriter) {
            bufferedWriter.close();
        }
        log.info("上传文件：{}", recordPath);
        scpAsync.scpFile(recordPath);
        log.info("设置统计KEY过期时间");
        setExpireTime(dailyFilesCount, tacID);
        log.info("策略执行完成：{}", tacInfo.get("tacName"));
        ptTaskMapper.tac_info_update(tacID, 1, null, "完成");
        // 发送短信
        Sender.sendSMS("20100038", tels(), message_content_to_maintainer(tacID));
    }

    /**
     * 获取文件数据
     *
     * @param filePath
     * @return
     */
    private List<String[]> getFileData(String filePath) {
        List<String> list = new FileReader(filePath).readLines();
        return list.stream().map(e ->
                e.split("\\|")).collect(Collectors.toList());
    }

    /**
     * 千人千面参数提取
     *
     * @param str
     * @return
     */
    private String[] getParams(String[] str) {
        // 第一位手机号排除
        String[] tplVars = new String[str.length - 1];
        System.arraycopy(str, 1, tplVars, 0, tplVars.length);
        return tplVars;
    }

    /**
     * 判断当前时间是否在可发送时段
     *
     * @return true在时间段内，false不在时间段内
     * @throws Exception
     */
    public boolean hourMinuteBetween() throws Exception {
        Date date = new Date();
        SimpleDateFormat simpleDateFormat_time = new SimpleDateFormat(DateUtil.DATE_TIME_FORMAT_HH_MI_SS);
        String time = simpleDateFormat_time.format(date);
        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");

        Date now = format.parse(time);
        Date start = format.parse("08:00:00");
        Date end = format.parse("20:00:00");

        long nowTime = now.getTime();
        long startTime = start.getTime();
        long endTime = end.getTime();

        return nowTime >= startTime && nowTime <= endTime;
    }

    /**
     * 删除文件
     *
     * @param path
     */
    private static void deleteFile(String path) {
        File file = new File(path);
        if (file.exists()) {
            log.info("文件：{}执行完成，删除", file.getName());
            file.delete();
        }
    }

    /**
     * 将文件重命名为csv格式
     *
     * @param path
     */
    private static String[] fileReName(String path) {
        File filePackage = new File(path);
        if (!filePackage.exists()) {
            log.error("当前路径下未发现待执行文件：{}", path);
            return null;
        }
        String[] files = new File(path).list();
        if (null == files || files.length == 0) {
            log.error("当前路径下未发现待执行文件：{}", path);
            return null;
        }
        for (String name : files) {
            String prefix = name.substring(name.lastIndexOf(".") + 1);
            if ("csv".equals(prefix)) {
                continue;
            }
            String filePath = path + "/" + name;
            String newPath = filePath + ".csv";
            File file = new File(filePath);
            File newFile = new File(newPath);
            file.renameTo(newFile);
        }
        return new File(path).list();
    }

    /**
     * 将文件重命名为csv格式
     *
     * @param path
     */
    private static String[] fileList(String path) {
        File filePackage = new File(path);
        if (!filePackage.exists()) {
            log.error("当前路径下未发现待执行文件：{}", path);
            return null;
        }
        String[] files = new File(path).list();
        if (null == files || files.length == 0) {
            log.error("当前路径下未发现待执行文件：{}", path);
            return null;
        }
        return files;
    }

    /**
     * 原子自增
     *
     * @return
     */
    private void redis_incr(String key_name) {
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        RedisAtomicLong redisAtomicLong = new RedisAtomicLong(key_name, redisTemplate.getConnectionFactory());
        redisAtomicLong.getAndIncrement();
    }

    /**
     * 提取{}中的参数并以数组形式返回
     *
     * @param str
     * @return
     */
    private String[] getParams(String str) {
        List<String> params = new ArrayList<>();
        // 提取{}中的参数
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            params.add(matcher.group());
        }
        // 提取不到参数
        if (null == params || params.size() == 0) {
            return null;
        }
        // 生成String[]
        String[] tplVars = new String[params.size()];
        for (int i = 0; i < params.size(); i++) {
            tplVars[i] = params.get(i);
        }
        log.info("提取到大括号中的参数:{}", tplVars.toString());
        return tplVars;
    }

    /**
     * 设置文件计数、成功数失败数过期时间
     *
     * @param dailyFilesCount
     * @param tacID
     */
    private void setExpireTime(String dailyFilesCount, String tacID) {
        // 成功数
        String key_success = tacID + "_" + DateUtil.getDateToday() + "_success";
        // 失败数
        String key_fail = tacID + "_" + DateUtil.getDateToday() + "_fail";
        redisTemplate.expire(dailyFilesCount, 3, TimeUnit.DAYS);
        redisTemplate.expire(key_success, 30, TimeUnit.DAYS);
        redisTemplate.expire(key_fail, 30, TimeUnit.DAYS);
    }

    /**
     * 维护人员短信模板
     *
     * @param target_id
     * @return
     */
    private String[] message_content_to_maintainer(String target_id) {
        List<String> vars = new ArrayList<>();
        vars.add("打工人");
        StringBuffer str = new StringBuffer();
        str.append("于");
        str.append(TimeUnit.DAYS);
        str.append("在咪咕运营服务云短信平台执行的编号为");
        str.append(target_id);
        str.append("的数字专辑场景已经完成");
        vars.add(String.valueOf(str));
        return list_to_array(vars);
    }

    /**
     * 转数组
     *
     * @param lists
     * @return
     */
    private String[] list_to_array(List<String> lists) {
        String[] array_temp = new String[lists.size()];
        String[] array = lists.toArray(array_temp);
        return array;
    }

    private String[] tels() {
        List<String> msisdns = new ArrayList<>();
        // 明兰
        msisdns.add("18280204903");
        // 王纯
        msisdns.add("15828381901");
        return list_to_array(msisdns);
    }
}
